package ee.rsi2m.goproject.model;

import java.util.*;

import android.util.Log;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;


import ee.rsi2m.goproject.R;
import ee.rsi2m.goproject.util.Constants;
import ee.rsi2m.goproject.util.Point;
import ee.rsi2m.goproject.util.StoneListHolder;

public class GoBoard implements DrawableBoard {
	private final int ScreenWidth, ScreenHeight;
	private final short NOTONBOARD = 32765;
	private boolean IIFDELETENEEDED = false;
	public final int size;
	private short board[][];

	public GoStone boardState[][];

	private Bitmap whiteStone;
	private Bitmap blackStone;
	public int turnCount = 1;
	public Point[][] dots;
	public List<Point> allpoints;
	public List<GoStone> stones = new ArrayList<GoStone>();
	public Paint k;
	private Bitmap background;
	private Rect backgroundRect;
	public float boardSize;

	public int getTurnCount() {
		return turnCount;
	}

	public void initializeBackground(Resources res) {
		this.backgroundRect = new Rect((int) dots[size - 1][1].getX() - 15,
				(int) dots[size - 1][1].getY() - 15,
				(int) dots[0][0].getX() + 15, (int) dots[0][0].getY() + 15);
		if (background == null)
			background = BitmapFactory.decodeStream(res
					.openRawResource(R.drawable.texture));
	}

	public Rect getBackgroundRect() {
		return backgroundRect;
	}

	public void setBackgroundRect(Rect backgroundRect) {
		this.backgroundRect = backgroundRect;
	}

	public float getBoardHeight() {
		return (float) (Math
				.abs(Math.sqrt((this.dots[0][0].getX() - this.dots[0][1].getX())
						* (this.dots[0][0].getX() - this.dots[0][1].getX()))
						+ (this.dots[0][0].getY() - this.dots[0][1].getY())
						* (this.dots[0][0].getY() - this.dots[0][1].getY())));

	}

	public float getBoardWidth() {
		return (float) (Math
				.abs(Math.sqrt((this.dots[21][0].getX() - this.dots[21][1]
						.getX())
						* (this.dots[21][0].getX() - this.dots[21][1].getX()))
						+ (this.dots[21][0].getY() - this.dots[21][1].getY())
						* (this.dots[21][0].getY() - this.dots[21][1].getY())));
	}

	public void draw(Canvas canvas) {
		canvas.drawBitmap(background, null, backgroundRect, null);
		// Log.w("myapp",""+this.dots.length/2-1);
		for (int i = 0; i < this.size * 2; i++) {
			canvas.drawLine(dots[i][0].getX(), dots[i][0].getY(),
					dots[i][1].getX(), dots[i][1].getY(), k);
		}
	}

	public GoBoard(int size, int screenWidth, int screenHeight) {
		this.k = new Paint();
		this.size = size;

		this.ScreenWidth = screenWidth;
		this.ScreenHeight = screenHeight;

		this.boardState = new GoStone[size + 1][size + 1];

		this.board = new short[this.size][this.size];

		/*
		 * dots[x][y] - is a Point type two dimensional array, where x - is the
		 * line number, y - is the point number(ex. 0 - starting point;1 -
		 * ending point) example : dots[3][0] - is a starting point of the third
		 * line dots[9][1] - is an ending point of the 9th line
		 */
		this.dots = new Point[this.size * 2][2];

		float zeroCorW;
		float zeroCorH;
		float boardOffset = (float) 0.95;
		float smallestSide = (float) ((this.ScreenWidth < this.ScreenHeight) ? ScreenWidth
				: ScreenHeight);
		float biggestSide = (float) ((this.ScreenWidth > this.ScreenHeight) ? ScreenWidth
				: ScreenHeight);
		float boardSide = smallestSide * boardOffset;

		float centralPositionH = this.ScreenHeight / 2;
		float centralPositionW = this.ScreenWidth / 2;
		float horizontalOffset = (this.ScreenHeight - boardSide) / 2;

		boardSize = boardSide / (this.size - 1);

		// Drawing of vertical 10th line;
		for (int i = 0; i < this.size; i++) {
			this.dots[size + i][0] = new Point(
					centralPositionW + boardSide / 2,
					(centralPositionH + boardSide / 2) - i * boardSize);
			this.dots[size + i][1] = new Point(
					centralPositionW - boardSide / 2,
					(centralPositionH + boardSide / 2) - i * boardSize);
		}

		for (int i = 0; i < this.size; i++) {
			this.dots[i][0] = new Point((centralPositionW + boardSide / 2) - i
					* boardSize, (centralPositionH + boardSide / 2));
			// Log.w("FirstPoint",""+this.dots[i][0].toString());
			this.dots[i][1] = new Point((centralPositionW + boardSide / 2) - i
					* boardSize, (centralPositionH - boardSide / 2));
		}

		allpoints = new ArrayList<Point>();

		for (int j = 0; j < this.size; j++) {
			getPointsFromOneLine(this.dots[j][0], allpoints);
		}

		initializeBoardState();

	}

	private void getPointsFromOneLine(Point startingPointOfTheLine,
			List<Point> array) {
		float y = startingPointOfTheLine.getY();
		for (int i = 0; i < this.size; i++) {
			array.add(new Point(startingPointOfTheLine.getX(), y));
			y -= this.getSquareSize();
			// Log.w("Coords", "X:" +startingPointOfTheLine.getX()+"Y:"+ "" +y);
		}
	}

	// TODO Refactoring & optimization needed!
	private void initializeBoardState() {
		int[] coordinates;
		for (int i = 0; i < allpoints.size(); i++) {
			coordinates = this.getPos(i);
			Log.d("Board state init", "Allpoints num is " + i + "x="
					+ coordinates[0] + " y=" + coordinates[1]);
			this.boardState[coordinates[0]][coordinates[1]] = new GoStone(
					allpoints.get(i).getX(), allpoints.get(i).getY(), false,
					false);

		}

	}

	// TODO REFACTORING!!!
	public int[] getPos(int i) {
		int size = this.size;
		int[] array = new int[2];
		i++;
		
		int k=i;
		int ai = 1;
		int kol = i;
		int ui = 1;

		// Calculates the X from bottom right corner, X starts with 1, not 0
		while (i > size) {

			i = i - size;
			ai++;

		}
		array[0] = ai;

		// Calculates the Y from bottom right corner, Y starts with 1, not 0
		if(ai==1){

				array[1]=k;

		}else{
			array[1]=k-(ai-1)*size;
		}

		return array;

	}

	private float getSquareSize() {
		return this.boardSize;
	}

	public float getStoneRadius() {
		return (float) (this.getSquareSize() * 0.95);

	}

	public Rect constructRectangleFromBoard() {
		Rect rectangle = new Rect((int) 0, (int) this.dots[0][0].getY() - 9,
				(int) this.dots[this.size - 1][1].getX() + 19,
				(int) this.dots[this.size - 1][1].getY() + 19);
		return rectangle;

	}

	public float getLenght(Point a, Point b) {
		return (float) (Math.abs(Math.sqrt((a.getX() - b.getX())
				* (a.getX() - b.getX()) + (a.getY() - b.getY())
				* (a.getY() - b.getY()))));
	}

	public Point[][] getDots() {
		/*
		 * return array int[][][] that contains start and end coordinates of
		 * lines on the board
		 */
		return this.dots;
	}

	public void setBoard(short[][] board) {
		this.board = board;
	}

	// REFACTORING REQUIRED!!!
	public float getDot(int line, boolean startEnd, boolean xY) {
		if (startEnd) {
			if (xY) {
				return this.dots[line][1].getY();
			} else {
				return this.dots[line][0].getY();
			}
		} else {
			if (xY) {
				return this.dots[line][1].getX();
			} else {
				return this.dots[line][0].getX();
			}
		}

	}

	public boolean getIfDeleteNeeded() {
		return this.IIFDELETENEEDED;
	}

	public boolean notSuicide(short x, short y) {
		/*
		 * check if near at list one empty cells or stone with the same color -
		 * return true (not a suicide)
		 */
		if (x < this.size - 1) {
			if (this.board[x + 1][y] == 0
					|| this.board[x + 1][y] % 2 == this.turnCount % 2) {
				return true;
			}
		}
		if (y < this.size - 1) {
			if (this.board[x][y + 1] == 0
					|| this.board[x][y + 1] % 2 == this.turnCount % 2) {
				return true;
			}
		}
		if (x > 0) {
			if (this.board[x - 1][y] == 0
					|| this.board[x - 1][y] % 2 == this.turnCount % 2) {
				return true;
			}

		}
		if (y > 0) {
			if (this.board[x][y - 1] == 0
					|| this.board[x][y - 1] % 2 == this.turnCount % 2) {
				return true;
			}
		}

		return false;
	}

	private boolean checkIfEat(short x, short y) {
		/*
		 * if after move at list one stone will be removed - remove it and
		 * return true
		 */
		this.IIFDELETENEEDED = true;

		return this.IIFDELETENEEDED;
	}

	private boolean notKo(short x, short y) {
		/*
		 * return true if after move position on the board will not repeat
		 * situation after previous move
		 */

		return false;
	}

	private boolean validateMove(short x, short y) {
		/*
		 * return true if move is valid
		 */
		this.IIFDELETENEEDED = false; // set IIFDELETENEEDEED to for checkIfEat
		// method;
		if (x != this.NOTONBOARD && y != this.NOTONBOARD) {
			if (this.board[x][y] == 0) {
				if (notKo(x, y)) {
					if (checkIfEat(x, y)) {
						return true;
					} else if (notSuicide(x, y)) {
						return true;
					}
				}
			}
		}
		return false;
	}

	public void initializeStoneAccordingToCounter(GoStone current) {
		if (getStoneColorByTurnCounter()) {
			current.initialize(Constants.WHITE, getWhiteStone(), turnCount);
		} else {
			current.initialize(Constants.BLACK, getBlackStone(), turnCount);
		}
	}

	public boolean getStoneColorByTurnCounter() {

		if (this.turnCount % 2 == 1) {
			return Constants.BLACK;

		} else {
			return Constants.WHITE;
		}
	}

	public void assignBitmapToStone(Resources resources) {
		if (getWhiteStone() == null) {
			Bitmap whiteStoneOr = BitmapFactory.decodeStream(resources
					.openRawResource(R.drawable.white32));
			setWhiteStone(GoStone.getResizedBitmap(whiteStoneOr,
					this.getStoneRadius()));
			whiteStoneOr.recycle();
		}
		if (getBlackStone() == null) {
			Bitmap blackStoneOr = BitmapFactory.decodeStream(resources
					.openRawResource(R.drawable.black32));
			setBlackStone(GoStone.getResizedBitmap(blackStoneOr,
					this.getStoneRadius()));
			blackStoneOr.recycle();
		}

	}

	public Bitmap getWhiteStone() {
		return whiteStone;
	}

	public void setWhiteStone(Bitmap whiteStone) {
		this.whiteStone = whiteStone;
	}

	public Bitmap getBlackStone() {
		return blackStone;
	}

	public void setBlackStone(Bitmap blackStone) {
		this.blackStone = blackStone;
	}

	public boolean notKo(Point point) {
		int[] coords = this.getPos(this.allpoints.indexOf(point));
		return !this.boardState[coords[0]][coords[1]].isKo();
	}
	
	public List<GoGroup> getAllGroups(StoneListHolder stoneListHolder){
		List<GoGroup> groups = new ArrayList<GoGroup>();
		for(GoStone stone : stoneListHolder.getStones()){
			if(!groups.contains(stone.getGroup()) && stone.isVisibilible()){
				groups.add(stone.getGroup());
			}
		}
		Log.w("Group Number","Size is:"+groups.size());
		return groups;
		
		
	}

	/*
	 * public boolean makeMove(float touchWidth, float touchHeight) {
	 * 
	 * if move valid made it and return true, if not - return false
	 * 
	 * short x = checkForBoardCorX(touchWidth), y =
	 * checkForBoardCorY(touchHeight); if (validateMove(x, y)) { this.lastMove =
	 * this.board; this.board[x][y] = this.turnCount; this.stones.add(new
	 * GoStone(this.turnCount, x, y)); this.turnCount++;
	 * 
	 * return true; } return false; }
	 */
}